﻿using System;
using System.Collections.Generic;
using HK.Core.Common.Data;
using HK.Core.Common.Protocol;
using HK.Core.Common.Protocol.Data;
using HK.Core.Network.Download.Protocol.Data;
using HK.Core.Serializable.Data;
using HK.Core.Serializable.Protocol.Data;
using HK.Core.Utils;
using UnityEngine;

namespace HK.Core.Network.Download.Data
{

    /// <summary>
    /// 下载描述异常
    /// </summary>
    public class DownloadDescException : Exception
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iMessage">Message</param>
        public DownloadDescException(string iMessage) : base(iMessage) {}
    }
    
    /// <summary>
    /// 下载文件描述
    /// </summary>
    [Serializable]
    public sealed class DownloadFileDesc : JsonData<DownloadFileDesc>, IEquatable<DownloadFileDesc>, IDownloadFileDesc
    {

#region Create

        /// <summary>
        /// 创建下载文件描述
        /// </summary>
        /// <param name="iPath">路径</param>
        /// <param name="iCrc">校验码 - Crc</param>
        /// <param name="iSize">文件大小</param>
        /// <returns>下载文件描述</returns>
        public static DownloadFileDesc Create(string iPath, uint iCrc, long iSize)
        {
            return new DownloadFileDesc()
            {
                path = iPath, crc = iCrc, size = iSize
            };
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private DownloadFileDesc() {}
        
        [SerializeField]
        private string path = null;
        /// <summary>
        /// 路径
        /// <para>* 相对路径</para>
        /// </summary>
        public string Path
        {
            get => path;
            private set => path = value;
        }
        
        [SerializeField]
        private uint crc = 0;
        /// <summary>
        /// 校验码 - Crc
        /// </summary>
        public uint Crc 
        {
            get => crc;
            private set => crc = value;
        }
        
        [SerializeField]
        private long size = 0L;
        /// <summary>
        /// 大小
        /// <para>* 单位 : Byte</para>
        /// </summary>
        public long Size 
        {
            get => size;
            private set => size = value;
        }

        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(path) && 0 < crc && 0 < size;
        
#region Equals

        /// <summary>
        /// 比较
        /// </summary>
        /// <param name="iDesc">描述信息</param>
        /// <returns>true:相同; false:不相同;</returns>
        public bool Equals(DownloadFileDesc iDesc)
        {
            if (null == iDesc || !iDesc.Valid) return false;

            if (UtilsString.Diff(path, iDesc.path)) return false;
            if (crc != iDesc.Crc) return false;
            if (size != iDesc.Size) return false;
            return true;
        }

#endregion

        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;
            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IDownloadFileDesc desc)
            {
                path = UpdateValue(path, desc.Path, iImport);
                crc = UpdateValue(crc, desc.Crc, iImport);
                size = UpdateValue(size, desc.Size, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            
            path = null;
            crc = 0;
            size = 0L;
        }
    }

    /// <summary>
    /// 下载描述
    /// </summary>
    [Serializable]
    public sealed class DownloadDesc : JsonData<DownloadDesc>, IDownloadDesc<DownloadFileDesc>
    {

#region Create

        /// <summary>
        /// 创建下载描述信息
        /// </summary>
        /// <param name="iDate">日期</param>
        /// <param name="iOriginVer">母包版本</param>
        /// <param name="iPathVer">补丁版本</param>
        /// <param name="iDescVer">描述版本</param>
        /// <param name="iTotalCount">总个数</param>
        /// <param name="iTotalSize">总大小</param>
        /// <param name="iCrc">校验码 - Crc</param>
        /// <param name="iAuthor">上传者/创建者</param>
        /// <returns>下载描述信息</returns>
        public static DownloadDesc Create(string iDate, string iOriginVer, string iPathVer, byte iDescVer,
            int iTotalCount, long iTotalSize, uint iCrc, string iAuthor = null)
        {
            if (string.IsNullOrEmpty(iDate) || string.IsNullOrEmpty(iOriginVer) ||
                string.IsNullOrEmpty(iPathVer)) return null;

            var originVer = SAssetVersion.Decode(iOriginVer);
            var patchVer = SAssetVersion.Decode(iPathVer);
            return new DownloadDesc()
            {
                dateTxt = iDate, originVer = originVer, patchVer = patchVer,
                descVer = iDescVer, totalCount = iTotalCount, totalSize = iTotalSize,
                crc = iCrc, author = iAuthor
            };
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private DownloadDesc() {}
        
        [SerializeField]
        private string dateTxt = null;
        /// <summary>
        /// 日期
        /// <para>* 测试时频繁更新文件，此信息对检查是否是CDN缓存重要</para>
        /// </summary>
        public string Date
        {
            get => dateTxt; 
            private set => dateTxt = value;
        } 
        
        [SerializeField]
        private string author = null;
        /// <summary>
        /// 上传者/创建者
        /// </summary>
        public string Author
        {
            get => author;
            private set => author = value;
        }
        
        [SerializeField]
        private SAssetVersion patchVer = SAssetVersion.Create();
        /// <summary>
        /// 补丁包版本
        /// </summary>
        public ISAssetVersion PathVer => patchVer;
        
        [SerializeField]
        private SAssetVersion originVer = SAssetVersion.Create();
        /// <summary>
        /// 母包版本
        /// </summary>
        public ISAssetVersion OriginVer => originVer;

        [SerializeField]
        private byte descVer = 0;
        /// <summary>
        /// 描述文件版本号
        /// </summary>
        public byte DescVer 
        {
            get => descVer;
            private set => descVer = value;
        }
        
        [SerializeField]
        private long totalSize = 0;
        /// <summary>
        /// 总大小
        /// <para>* 单位 : Byte</para>
        /// </summary>
        public long TotalSize 
        {
            get => totalSize;
            private set => totalSize = value;
        }
        
        [SerializeField]
        private uint crc = 0;
        /// <summary>
        /// 校验码 - Crc
        /// </summary>
        public uint Crc 
        {
            get => crc;
            private set => crc = value;
        }
        
        [SerializeField]
        private int totalCount = 0;
        /// <summary>
        /// 总个数
        /// <para>* 既:下载列表文件数</para>
        /// </summary>
        public int TotalCount 
        {
            get => totalCount;
            private set => totalCount = value;
        }

        [SerializeField]
        private List<DownloadFileDesc> list = new List<DownloadFileDesc>();
        /// <summary>
        /// 下载文件列表
        /// </summary>
        public List<DownloadFileDesc> List => list;

        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(dateTxt) && 0 < crc;

        /// <summary>
        /// 追加下载文件描述信息
        /// </summary>
        /// <param name="iFileDesc">文件描述信息</param>
        public void AddDownloadFileDesc(DownloadFileDesc iFileDesc)
        {
            if (null == iFileDesc)
            {
                throw new DownloadDescException("The desc of file is null!");
            }   
            
            if(!iFileDesc.Valid)
            {
                throw new DownloadDescException($"The desc of file is invalid!(Desc:{iFileDesc.ToString()})");
            }

            if (list.Exists(iO => iO.Equals(iFileDesc)))
            {
                throw new DownloadDescException($"The desc of file had been exist!(Desc:{iFileDesc.ToString()})");
            }
            list.Add(iFileDesc);
        }
        
        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;
            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IDownloadDesc<DownloadFileDesc> desc)
            {
                dateTxt = UpdateValue(dateTxt, desc.Date, iImport);
                author = UpdateValue(author, desc.Author, iImport);

                dirty = patchVer.ApplyData(desc.PathVer);
                if (dirty && !iImport)
                {
                    Dirty = true;
                }
                
                dirty = originVer.ApplyData(desc.OriginVer);
                if (dirty && !iImport)
                {
                    Dirty = true;
                }
                descVer = UpdateValue(descVer, desc.DescVer, iImport);
                totalSize = UpdateValue(totalSize, desc.TotalSize, iImport);
                totalCount = UpdateValue(totalCount, desc.TotalCount, iImport);
                crc = UpdateValue(crc, desc.Crc, iImport);
                
                dirty = UtilsArray.Diff<DownloadFileDesc>(list, desc.List);
                if (dirty)
                {
                    list.Clear();
                    list.AddRange(desc.List);

                    if (!iImport)
                    {
                        Dirty = true;
                    }
                }
            }

            return Dirty;
        }
        
        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            
            dateTxt = null;
            author = null;
            
            originVer.Clear();
            patchVer.Clear();
            descVer = 0;

            totalCount = 0;
            totalSize = 0L;
            crc = 0;
            
            list.Clear();
        }
    }
}
